www.gusucode.com > VC++ 编写软件自动升级服务源代码 > VC++ 编写软件自动升级服务源代码/gusucode/updater_src0.8.1.6/UpdateProcess.cpp

    /********************************************************************
	created:	2005/03/02
	created:	2:3:2005   9:24
	filename: 	UpdateProcess.cpp
	file path:	Updater
	file base:	UpdateProcess
	file ext:	cpp
	author:		Geert van Horrik
	
	purpose:	
*********************************************************************/

//*********************************************************************
// INCLUDES
//*********************************************************************

#include "stdafx.h"
#include "Updater.h"
#include "UpdateProcess.h"

// Registry support
#include "Registry.h"

// Actions
#include "CheckVersion.h"
#include "Protection.h"
#include "SelectUpdate.h"
#include "Download.h"
#include "Install.h"
#include "Rollback.h"

// Sections support
#include "Sections.h"

// About dlg
#include "AboutDlg.h"

//*********************************************************************
// MESSAGE MAP
//*********************************************************************

BEGIN_MESSAGE_MAP(CUpdateProcess, CWinThread)
	ON_THREAD_MESSAGE(WMU_THREADMESSAGE, OnThreadMessage)
	ON_THREAD_MESSAGE(WMU_TASK_COMPLETE, OnTaskComplete)
	ON_THREAD_MESSAGE(WMU_ERROR, OnError)
	ON_THREAD_MESSAGE(WMU_WARNING, OnWarning)
	ON_THREAD_MESSAGE(WMU_WARNING_COMPLETE, OnWarningComplete)
	ON_THREAD_MESSAGE(WMU_QUESTION, OnQuestion)
	ON_THREAD_MESSAGE(WMU_QUESTION_ANSWER, OnQuestionAnswer)
	ON_THREAD_MESSAGE(WMU_UPDATE_GUI, OnUpdateGUI)
	ON_THREAD_MESSAGE(WMU_UPDATE_GUI_FILEPROGRESS, OnUpdateGUIFileProgress)
	ON_THREAD_MESSAGE(WMU_UPDATE_GUI_TOTALPROGRESS, OnUpdateGUITotalProgress)
	ON_THREAD_MESSAGE(WMU_UPDATE_GUI_PROTECTION, OnUpdateGUIProtection)
	ON_THREAD_MESSAGE(WMU_SWITCHUI, OnSwitchUI)
	ON_THREAD_MESSAGE(WMU_SHOWABOUT, OnShowAbout)
END_MESSAGE_MAP()

//*********************************************************************
// CONSTRUCTOR & DESTRUCTOR
//*********************************************************************

IMPLEMENT_DYNCREATE(CUpdateProcess, CWinThread)

//=====================================================================

CUpdateProcess::CUpdateProcess()
{
}

//=====================================================================

CUpdateProcess::~CUpdateProcess()
{
}

//*********************************************************************
// PUBLIC FUNCTIONS
//*********************************************************************

BOOL CUpdateProcess::InitInstance()
{
	// Initialise data
	m_iCurrentTask = TASK_INIT;
	m_pCurrentUI = NULL;
	m_pCurrentThread = NULL;
	m_bFinished = false;
	m_bRollback = false;
	m_bInstallComplete = false;
	m_bAllowUIChange = true;
	m_bUpdateCancelled = false;

	// Initialise singleton classes
	m_pSettings = CSettings::Instance();
	m_pLanguage = CLanguage::Instance();
	m_pUpdateInfo = CUpdateInfo::Instance();
	m_pFunctions = CFunctions::Instance();
	m_pEventInfo = CEventInfo::Instance();
	m_pLog = CLog::Instance();
	m_pPath = CPath::Instance();

	// Read section file
	CSections * pSections = CSections::Instance();
	pSections->ReadFile(m_pPath->GetPathUpdater() + FILENAME_SECTIONS);

	// Initialize internet class
	CInternet * pInternet = CInternet::Instance();
	pInternet->SetProxyAuthentication(m_pSettings->GetProxyUsername(), m_pSettings->GetProxyPassword());
	pInternet->SetHttpAuthentication(m_pSettings->GetHttpUsername(), m_pSettings->GetHttpPassword());
	pInternet->SetFtpAuthentication(m_pSettings->GetFtpUsername(), m_pSettings->GetFtpPassword());
	pInternet->SetTimeOut(m_pSettings->GetTimeout());

	// Start downloading custom language file
	pInternet->SetParent(this);

	// Init task is ready
	PostThreadMessage(WMU_TASK_COMPLETE, TASK_INIT, 0);

	// Start message pump
	return TRUE;
}

//=====================================================================

int CUpdateProcess::ExitInstance()
{
	// Declare variables
	CInternet * pInternet = CInternet::Instance();
	CRollbackInfo * pRollback = CRollbackInfo::Instance();
	CString sDirectory;

	// Destroy the User Interface
	DestroyUserInterface();

	// Is the current thread closed?
	if (!IsBadReadPtr(m_pCurrentThread, sizeof(CWinThread *)))
		m_pCurrentThread->PostThreadMessage(WM_QUIT, 0, 0);

	// Handle OnClose event only if we are successful
	if ((m_iCurrentTask != TASK_ROLLBACK) && m_bFinished)
	{
		HandleEvent(m_pEventInfo->GetEventInfo(EVENT_ONCLOSE));
	}

	// Check if the update is completly ready
	if (m_bInstallComplete)
	{
		FinishUpdate();
	}

	// Clear some memory (singleton classes won't be cleared)
	m_pEventInfo->CleanUp();
	m_pUpdateInfo->CleanUp();
	pInternet->CleanUp();
	pRollback->CleanUp();

	// Auto clean-up thread
	m_bAutoDelete = TRUE;

	// Exit this thread, which will end application
	return CWinThread::ExitInstance();
}

//*********************************************************************
// PRIVATE FUNCTIONS
//*********************************************************************

void CUpdateProcess::StartTask(int iTask)
{
	// If user cancelled update
	if (m_bUpdateCancelled)
	{
		// Only allow rollback
		if (iTask != TASK_ROLLBACK)
			return;
	}

	// What task should we start?
	switch (iTask)
	{
	case TASK_NEXTTASK:
		// Start next task
		m_iCurrentTask++;
		break;

	default:
		// Set task
		m_iCurrentTask = iTask;	
	}

	// Check if process is cancelled
	if (m_bUpdateCancelled)
	{
		// We are only allowed to handle TASK_ROLLBACK
		if (m_iCurrentTask != TASK_ROLLBACK)
			return;
	}

	// Check what task to start now
	switch (m_iCurrentTask)
	{
	case TASK_CHECKVERSION:
		// Log
		m_pLog->Log(_T("starting task: TASK_CHECKVERSION"));

		// Check version
		if (!CheckVersion())
		{
			// Stop executing this thread
			PostThreadMessage(WM_QUIT, 0, 0);
			return;
		}
		break;

	case TASK_HTMLMESSAGE:
		// Log
		m_pLog->Log(_T("starting task: TASK_HTMLMESSAGE"));

		// Check if we should show dialog
		if (m_pUpdateInfo->GetHtmlMessageURL().IsEmpty())
		{
			// Post message to start next task
			PostThreadMessage(WMU_TASK_COMPLETE, TASK_HTMLMESSAGE, 0);
			return;
		}

		// Handle BeforeHtmlMessage event
		HandleEvent(m_pEventInfo->GetEventInfo(EVENT_BEFOREHTMLMESSAGE));

		// Always send this message to user interface
		SendMessageToUI(WMU_TASK_START, TASK_HTMLMESSAGE, 0);

		// Now wait until user has watched the html message
		break;

	case TASK_LICENSE:
		// Log
		m_pLog->Log(_T("starting task: TASK_LICENSE"));

		// Check if we should show dialog
		if (m_pUpdateInfo->GetLicense().IsEmpty())
		{
			// Post message to start next task
			PostThreadMessage(WMU_TASK_COMPLETE, TASK_LICENSE, 0);
			return;
		}

		// Handle BeforeLicense event
		HandleEvent(m_pEventInfo->GetEventInfo(EVENT_BEFORELICENSE));

		// Always send this message to user interface
		SendMessageToUI(WMU_TASK_START, TASK_LICENSE, 0);

		// Now wait until user has agreed to the license
		break;

	case TASK_PROTECTION:
		// Log
		m_pLog->Log(_T("starting task: TASK_PROTECTION"));

		// Check if we should show dialog
		if (m_pUpdateInfo->GetProtectionRegistrationDll().IsEmpty() && (m_pUpdateInfo->GetProtectionHashCount() == 0))
		{
			// Post message to start next task
			PostThreadMessage(WMU_THREADMESSAGE, THREAD_STARTNEXTTASK, 0);
			return;
		}

		// Check protection
		if (!Protect())
		{
			// Stop executing this thread
			PostThreadMessage(WM_QUIT, 0, 0);
			return;
		}
		break;

	case TASK_SELECTUPDATE:
		// Log
		m_pLog->Log(_T("starting task: TASK_SELECTUPDATE"));

		// Check if we should show dialog
		if (m_pUpdateInfo->GetSectionCount() <= 0)
		{
			// Post message to start next task
			PostThreadMessage(WMU_TASK_COMPLETE, TASK_SELECTUPDATE, 0);
			return;
		}

		// Handle BeforeSelectUpdate event
		HandleEvent(m_pEventInfo->GetEventInfo(EVENT_BEFORESELECTUPDATE));

		// Always send this message to user interface
		SendMessageToUI(WMU_TASK_START, TASK_SELECTUPDATE, 0);

		// Now wait until user has selected the updates
		break;

	case TASK_DOWNLOAD:
		// Log
		m_pLog->Log(_T("starting task: TASK_DOWNLOAD"));

		// Download files
		if (!Download())
		{
			// Stop executing this thread
			PostThreadMessage(WM_QUIT, 0, 0);
			return;
		}
		break;
		
	case TASK_INSTALL:
		// Log
		m_pLog->Log(_T("starting task: TASK_INSTALL"));

		// Install files
		if (!Install())
		{
			// Stop executing this thread
			PostThreadMessage(WM_QUIT, 0, 0);
			return;
		}
		break;

	case TASK_ROLLBACK:
		// Log
		m_pLog->Log(_T("starting task: TASK_ROLLBACK"));

		// Install is not ready anymore!
		m_bInstallComplete = false;

		// Rollback actions
		Rollback();
		break;
	}
}

//=====================================================================

bool CUpdateProcess::CheckVersion()
{
	// Always send this message to user interface
	SendMessageToUI(WMU_TASK_START, TASK_CHECKVERSION, 0);

	// Start thread
	CCheckVersion * pCheckVersion;
	pCheckVersion = (CCheckVersion *)AfxBeginThread(RUNTIME_CLASS(CCheckVersion),
													THREAD_PRIORITY_NORMAL,
													0,
													0,
													NULL);

	// Check if thread is successfully started
	if (pCheckVersion != NULL)
	{
		// Inform who the parent is
		pCheckVersion->PostThreadMessage(WMU_SETPARENT, (WPARAM)this, 0);

		// Now really start thread
		pCheckVersion->PostThreadMessage(WMU_THREADMESSAGE, THREAD_START, 0);

		// Set current thread
		m_pCurrentThread = pCheckVersion;

		// We are successful
		return true;
	}
	else
	{
		// Thread did not start successful
		return false;
	}
}

//=====================================================================

bool CUpdateProcess::Protect()
{
	// Always send this message to user interface
	SendMessageToUI(WMU_TASK_START, TASK_PROTECTION, 0);

	// Start thread
	CProtection * pProtection;
	pProtection = (CProtection *)AfxBeginThread(RUNTIME_CLASS(CProtection),
													THREAD_PRIORITY_NORMAL,
													0,
													0,
													NULL);

	// Check if thread is successfully started
	if (pProtection != NULL)
	{
		// Inform who the parent is
		pProtection->PostThreadMessage(WMU_SETPARENT, (WPARAM)this, 0);

		// Now really start thread
		pProtection->PostThreadMessage(WMU_THREADMESSAGE, THREAD_START, 0);

		// Set current thread
		m_pCurrentThread = pProtection;

		// We are successful
		return true;
	}
	else
	{
		// Thread did not start successful
		return false;
	}

	return true;
}

//=====================================================================

bool CUpdateProcess::SelectUpdate()
{
	// Declare variables
	CSections * pSections = CSections::Instance();
	CString arrSectionsToUpdate[MAX_SECTIONS];
	int i, j, iSectionsToUpdate = 0;

	// When check separately is used, no sections can be checked!
	if (m_pUpdateInfo->GetCheckSeparately())
	{
		return true;
	}

	// Log
	m_pLog->Log(_T("CHECKING FOR SELECTED SECTIONS BY USER"));

	// Check which sections should be updated
	for (i = 0; i < m_pUpdateInfo->GetSectionCount(); i++)
	{
		if (m_pUpdateInfo->GetSectionData(i)->GetSelectedByUser())
		{
			// This section should be updated, add name to array
			arrSectionsToUpdate[iSectionsToUpdate++] = m_pUpdateInfo->GetSectionData(i)->GetName();

			// Log
			m_pLog->Log(_T("  * ") + m_pUpdateInfo->GetSectionData(i)->GetName());
		}
	}

	// Calculate the files that should be updated
	for (i = 0; i < m_pUpdateInfo->GetFileCount(); i++)
	{
		// Set default to no update
		m_pUpdateInfo->GetFileData(i)->SetUpdateFile(false);

		// Is the section empty?
		if (m_pUpdateInfo->GetFileData(i)->GetSection().IsEmpty())
		{
			// We should update this file
			m_pUpdateInfo->GetFileData(i)->SetUpdateFile(true);
		}
		else
		{
			// Check to which section this file belongs
			for (j = 0; j < iSectionsToUpdate; j++)
			{
				// Should we update?
				if (arrSectionsToUpdate[j] == m_pUpdateInfo->GetFileData(i)->GetSection())
					m_pUpdateInfo->GetFileData(i)->SetUpdateFile(true);
			}
		}
	}

	// Log
	m_pLog->Log(_T("NEW VERSIONS FOR SELECTED SECTIONS"));

	// Set new version for the sections that will be updated
	for (i = 0; i < iSectionsToUpdate; i++)
	{
		// Get the newest version
		for (j = 0; j < m_pUpdateInfo->GetSectionCount(); j++)
		{
			// Do we have the right one?
			if (m_pUpdateInfo->GetSectionData(j)->GetName() == arrSectionsToUpdate[i])
			{
				// Set version
				pSections->SetSectionVersion(arrSectionsToUpdate[i], m_pUpdateInfo->GetSectionData(j)->GetVersion());

				// Log new section version
				m_pLog->Log(_T("  * ") + m_pUpdateInfo->GetSectionData(j)->GetName() + _T(" -> ") +
					m_pUpdateInfo->GetSectionData(j)->GetVersion());
			}
		}
	}

	// No thread needs to be started
	return true;
}

//=====================================================================

bool CUpdateProcess::Download()
{
	// Handle BeforeDownload task
	HandleEvent(m_pEventInfo->GetEventInfo(EVENT_BEFOREDOWNLOAD));

	// Always send this message to user interface
	SendMessageToUI(WMU_TASK_START, TASK_DOWNLOAD, 0);

	// Start thread
	CDownload * pDownload;
	pDownload = (CDownload *)AfxBeginThread(RUNTIME_CLASS(CDownload),
											THREAD_PRIORITY_NORMAL,
											0,
											0,
											NULL);
	
	// Check if thread is successfully started
	if (pDownload != NULL)
	{
		// Inform who the parent is
		pDownload->PostThreadMessage(WMU_SETPARENT, (WPARAM)this, 0);

		// Now really start thread
		pDownload->PostThreadMessage(WMU_THREADMESSAGE, THREAD_START, 0);

		// Set current thread
		m_pCurrentThread = pDownload;

		// We are successful
		return true;
	}
	else
	{
		// Thread did not start successful
		return false;
	}
}

//=====================================================================

bool CUpdateProcess::Install()
{
	// Handle BeforeInstall event
	HandleEvent(m_pEventInfo->GetEventInfo(EVENT_BEFOREINSTALL));

	// Always send this message to user interface
	SendMessageToUI(WMU_TASK_START, TASK_INSTALL, 0);

	// Start thread
	CInstall * pInstall;
	pInstall = (CInstall *)AfxBeginThread(RUNTIME_CLASS(CInstall),
										  THREAD_PRIORITY_NORMAL,
										  0,
										  0,
										  NULL);
	
	// Check if thread is successfully started
	if (pInstall != NULL)
	{
		// Inform who the parent is
		pInstall->PostThreadMessage(WMU_SETPARENT, (WPARAM)this, 0);

		// Now really start thread
		pInstall->PostThreadMessage(WMU_THREADMESSAGE, THREAD_START, 0);

		// Set current thread
		m_pCurrentThread = pInstall;

		// We are successful
		return true;
	}
	else
	{
		// Thread did not start successful
		return false;
	}
}

//=====================================================================

void CUpdateProcess::Rollback()
{
	// Rollback is used
	m_bRollback = true;

	// Handle BeforeRollback event
	HandleEvent(m_pEventInfo->GetEventInfo(EVENT_BEFOREROLLBACK));

	// Always send this message to user interface
	SendMessageToUI(WMU_TASK_START, TASK_ROLLBACK, 0);

	// Should we rollback?
	if (m_pUpdateInfo->GetRollbackEnabled() && !m_bFinished)
	{
		// Rollback all actions
		CRollback * pRollback;
		pRollback = (CRollback *)AfxBeginThread(RUNTIME_CLASS(CRollback),
												THREAD_PRIORITY_NORMAL,
												0,
												0,
												NULL);

		// Check if thread is successfully started
		if (pRollback != NULL)
		{
			// Inform who the parent is
			pRollback->PostThreadMessage(WMU_SETPARENT, (WPARAM)this, 0);
			
			// Now really start thread
			pRollback->PostThreadMessage(WMU_THREADMESSAGE, THREAD_START, 0);
			
			// Set current thread
			m_pCurrentThread = pRollback;
		}
	}
	else
	{
		// We shouldn't rollback, but in some cases, we don't know IF we
		// should rollback, i.e. when retrieving the update file. So, 
		// just send message that rollback is finished

		// Always set everything to 100 % at the end
		PostThreadMessage(WMU_UPDATE_GUI_TOTALPROGRESS, 100, 0);
		
		// Send message to parent that we are ready
		PostThreadMessage(WMU_TASK_COMPLETE, TASK_ROLLBACK, 0);
	}
}

//=====================================================================

void CUpdateProcess::FinishUpdate()
{
	// Declare variables
	CString sDirectory, sTemp, sSectionFile;
	bool bReboot = false;
	CShortcutData * pData;
	CRestoreInfo * pRestoreInfo = CRestoreInfo::Instance();

	// Initialize COM
	HRESULT hRes = CoInitialize(NULL);

	if (SUCCEEDED(hRes))
	{
		// Should we create shortcuts?
		if (m_pUpdateInfo->GetShortcutsCreateShortcuts())
		{
			// Create the shortcuts
			for (int i = 0; i < m_pUpdateInfo->GetShortcutCount(); i++)
			{
				// Get data
				pData = m_pUpdateInfo->GetShortcutData(i);

				// Log
				m_pLog->Log(_T("create shortcut ") + pData->GetDescription());

				// Create shortcut
				CreateShortcut(pData->GetTargetFile(), pData->GetParameters(), pData->GetLinkFile(),
					pData->GetDescription(), pData->GetShowMode(), pData->GetWorkingDirectory(),
					pData->GetIconFile(), pData->GetIconIndex());
			}
		}
	}

	// De-initialize COM
	CoUninitialize();

	// Write new version to settings.ini
#ifndef _DEBUG
	// Change version in settings.ini
	if (!PathFileExists(m_pSettings->GetAppVersion()))
	{
		// Only change new version when there really is a new version
		if (m_pFunctions->CompareVersions(m_pUpdateInfo->GetNewVersion(), m_pSettings->GetAppVersion()) == COMPAREVERSION_FIRSTLARGER)
		{
			// It is not a file, we can overwrite the settings
			WritePrivateProfileString(_T("APPLICATION"), _T("version"), m_pUpdateInfo->GetNewVersion(),
				CInternalData::Instance()->GetSettingsFile());

			// Log
			m_pLog->Log(_T("new version (") + m_pUpdateInfo->GetNewVersion() + _T(") is updated in settings file"));
		}
	}

	// Write all data to section file
	sSectionFile = m_pPath->GetPathUpdater() + FILENAME_SECTIONS;
	sSectionFile.Replace(_T("%name%"), m_pSettings->GetAppName());
	CSections * pSections = CSections::Instance();
	pSections->WriteFile(sSectionFile);
	
	// Log
	m_pLog->Log(_T("written section file to ") + FILENAME_SECTIONS);
#endif

	// If rollback is enabled, enable restore
	if (m_pUpdateInfo->GetRollbackEnabled())
	{
		// Log
		m_pLog->Log(_T("rollback is enabled, save restore information to ") + FILENAME_RESTORE);

		// Copy all information from rollback to restore
		pRestoreInfo->RetrieveInformationFromRollback();
		
		// Write information to file
		pRestoreInfo->WriteToFile(m_pPath->GetPathUpdaterTemp() + FILENAME_RESTORE);
	}

	// Delete all temporary files (don't do this anymore since restore is possible)
	CGarbageCollector::Instance()->CleanGarbage();

	// Log
	m_pLog->Log(_T("temporary update files deleted"));

	// Should we restart anything?
	switch (m_pUpdateInfo->GetCloseApplication())
	{
	case CLOSE_RESTART:
		// Log
		m_pLog->Log(_T("restart application (") + m_pSettings->GetAppLocation() + _T(")"));
		
		// Restart application
		if (PathFileExists(m_pSettings->GetAppLocation()))
		{
			// Is the application closed?
			if (CInternalData::Instance()->GetApplicationClosed())
			{
				// Prepare shellExecutInfo
				sDirectory = m_pPath->ExtractFilePath(m_pSettings->GetAppLocation());
				SHELLEXECUTEINFO ShRun = {0};
				ShRun.cbSize = sizeof(SHELLEXECUTEINFO);
				ShRun.fMask = SEE_MASK_NOCLOSEPROCESS;
				ShRun.hwnd = NULL;
				ShRun.lpVerb = NULL;
				ShRun.lpFile = m_pSettings->GetAppLocation();
				ShRun.lpDirectory = sDirectory;
				ShRun.nShow = SW_SHOW;
				ShRun.hInstApp = NULL;

				// Is app already running?
				if (!m_pFunctions->ApplicationRunning(m_pSettings->GetAppLocation()))
				{
					// Execute the file with the parameters
					ShellExecuteEx(&ShRun);
				}
			}
		}
		break;

	case CLOSE_RESTARTSYSTEM:
		// Log
		m_pLog->Log(_T("restart system"));

		// Reboot system
		bReboot = true;
		break;

	case CLOSE_RESTARTSYSTEMAPP:
		// Log
		m_pLog->Log(_T("restart system and run application (") + m_pSettings->GetAppLocation() + _T(") at next startup"));

		// Set application into startup path
		CRegistry reg;
		reg.CreateKey(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce"));
		reg.Open(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce"));
		reg.Write(_T("Updater application restart"), m_pSettings->GetAppLocation());
		reg.Close();

		// Reboot system
		bReboot = true;
		break;
	}

	// Should we reboot?
	if (bReboot)
	{
		// Restart system
		if ((m_pSettings->GetRunmode() == MODE_FULL) || (m_pSettings->GetRunmode() == MODE_SILENT))
		{
			sTemp.Format(m_pLanguage->GetString(IDS_INFORMATION_RESTARTSYSTEM_EXPLANATION), m_pSettings->GetAppName());
			CInformationDlg dlgInformation(m_pLanguage->GetString(IDS_INFORMATION_TITLE), m_pLanguage->GetString(IDS_INFORMATION_RESTARTSYSTEM_TITLE),
				sTemp, m_pLanguage->GetString(IDS_GENERAL_OK));
			dlgInformation.DoModal();
		}

		// Restart system
		ExitWindowsEx(EWX_REBOOT, 0);
	}
}

//=====================================================================

void CUpdateProcess::SetMode(CString sMode)
{
	// Check if we are allowed to change UI
	if (!m_bAllowUIChange)
	{
		// Log
		m_pLog->Log(_T("CHANGING OF USER INTERFACE NOT ALLOWED"));

		// Exit functon
		return;
	}

	// Log
	m_pLog->Log(_T("START CHANGING USER INTERFACE"));
	m_pLog->Log(_T("  destroy existing user interface"));

	if (sMode == MODE_FULL)
	{
		// Destroy current user interface
		DestroyUserInterface();

		// Log
		m_pLog->Log(_T("  create new user interface: FULL MODE"));

		// Start full mode
		m_pCurrentUI = new CModeFullSheet(this, m_iCurrentTask);
		// Create window
		CModeFullSheet * pTemp = (CModeFullSheet *)m_pCurrentUI;
		pTemp->Create();

		// Set new mode
		m_sMode = sMode;
	}

	// Check what user interface should be loaded
	if (sMode == MODE_SILENT || sMode == MODE_VERYSILENT)
	{
		// Destroy current user interface
		DestroyUserInterface();

		// Log
		m_pLog->Log(_T("  create new user interface: SILENT MODE"));

		// Start silent mode
		m_pCurrentUI = new CModeSilent(this, m_iCurrentTask, sMode);

		// Create window
		CModeSilent * pTemp = (CModeSilent *)m_pCurrentUI;
		pTemp->Create(IDD_SILENT, (CWnd *)this);

		// Set new mode
		m_sMode = sMode;
	}

	if (sMode == MODE_HIDDEN)
	{
		// Destroy current user interface
		DestroyUserInterface();

		// Log
		m_pLog->Log(_T("  create new user interface: HIDDEN MODE"));

		// Start hidden mode
		m_pCurrentUI = new CModeHidden(this, m_iCurrentTask);

		// Create window
		CModeHidden * pTemp = (CModeHidden *)m_pCurrentUI;
		pTemp->Create(IDD_HIDDEN, (CWnd *)this);

		// Set new mode
		m_sMode = sMode;
	}

	// Log
	m_pLog->Log(_T("END CHANGING USER INTERFACE"));
}

//=====================================================================

void CUpdateProcess::DestroyUserInterface()
{
	// If we have a user interface
	if (m_pCurrentUI != NULL)
	{
		// Destroy window
		m_pCurrentUI->DestroyWindow();

		// Delete object
		delete m_pCurrentUI;

		// Set object to NULL
		m_pCurrentUI = NULL;
	}
}

//=====================================================================

void CUpdateProcess::SendMessageToUI(UINT message, WPARAM wParam, LPARAM lParam,
									 bool bSaveMessage /* = true */)
{
	// Do we have a valid UI?
	if (m_pCurrentUI != NULL)
	{
		// Save message
		if (bSaveMessage)
		{
			m_oLastUIMessage.message = message;
			m_oLastUIMessage.wParam = wParam;
			m_oLastUIMessage.lParam = lParam;
		}

		// Send message
		m_pCurrentUI->PostMessage(message, wParam, lParam);
	}
}

//=====================================================================

void CUpdateProcess::HandleEvent(CEventData * pEventData)
{
	// Declare variables
	CString sType, sDirectory;

	// Check if we have any event data
	if (pEventData == NULL)
		return;

	// Event is valid, check what actions to perform
	for (int i = 0; i < pEventData->GetActionCount(); i++)
	{
		// Get action type
		sType = pEventData->GetAction(i)->GetType();

		// Check what action to perform
		if (sType == EVENTACTION_CLOSE)
		{
			// Log
			m_pLog->Log(_T("handle eventaction CLOSE"));

			// Switch to hidden mode
			SetMode(MODE_HIDDEN);

			// Don't allow to switch to other UI again
			m_bAllowUIChange = false;

			// Check if the update is completly ready
			if (m_bInstallComplete || m_bFinished)
			{
				// Finish nicely
				PostThreadMessage(WMU_THREADMESSAGE, THREAD_FINISH, 0);
			}
			else
			{
				// Rollback, then automatically close
				PostThreadMessage(WMU_THREADMESSAGE, THREAD_CANCEL, 0);
			}
		}

		if (sType == EVENTACTION_RUN)
		{
			// Get right action
			CEventActionRun * pAction = (CEventActionRun *)pEventData->GetAction(i);

			// Check if the user tries to open a website
			if (!m_pFunctions->IsWebAddress(pAction->GetRunLocation()))
			{
				// Check if file exists
				if (!PathFileExists(pAction->GetRunLocation()))
				{
					return;
				}
			}
			
			// Prepare shellExecutInfo
			sDirectory = m_pPath->ExtractFilePath(pAction->GetRunLocation());
			SHELLEXECUTEINFO ShRun = {0};
			ShRun.cbSize = sizeof(SHELLEXECUTEINFO);
			ShRun.fMask = SEE_MASK_NOCLOSEPROCESS;
			ShRun.hwnd = NULL;
			ShRun.lpVerb = NULL;
			ShRun.lpFile = pAction->GetRunLocation();
			ShRun.lpParameters = pAction->GetParameters();
			ShRun.lpDirectory = sDirectory;
			ShRun.nShow = SW_SHOW;
			ShRun.hInstApp = NULL;

			// Log
			m_pLog->Log(_T("handle eventaction RUN (") + pAction->GetRunLocation() + _T(")"));

			// Execute file
			ShellExecuteEx(&ShRun);
		}

		if (sType == EVENTACTION_SETMODE)
		{
			// Get right action
			CEventActionSetMode * pAction = (CEventActionSetMode *)pEventData->GetAction(i);

			// Log
			m_pLog->Log(_T("handle eventaction SETMODE"));

			// Set mode
			SetMode(pAction->GetMode());
		}

		if (sType == EVENTACTION_SKIPNEXTTASK)
		{
			// Log
			m_pLog->Log(_T("handle eventaction SKIPNEXTTASK"));

			// Skip next task
			PostThreadMessage(WMU_THREADMESSAGE, THREAD_SKIPNEXTTASK, 0);
		}

		if (sType == EVENTACTION_STARTNEXTTASK)
		{
			// Log
			m_pLog->Log(_T("handle eventaction STARTNEXTTASK"));

			// Skip next task
			PostThreadMessage(WMU_THREADMESSAGE, THREAD_STARTNEXTTASK, 0);
		}

		if (sType == EVENTACTION_CLOSEAPPLICATION)
		{
			// Get right action
			CEventActionCloseApplication * pAction = (CEventActionCloseApplication *)pEventData->GetAction(i);

			// Log
			m_pLog->Log(_T("handle eventaction CLOSEAPPLICATION"));

			// Check if application is running
			if (m_pFunctions->ApplicationRunning(pAction->GetFilename()))
			{
				// Should we ask user?
				if (pAction->GetAskUser())
				{
					// TEMPORARY SOLUTION - BEGIN
					/* 

					This should be asked to the user interface, but there is no solution for this one
					yet

					 */
					// Set up confirmation text
					CString sTemp;
					sTemp.Format(m_pLanguage->GetString(IDS_CONFIRMATION_CLOSEAPPLICATION_EXPLANATION),
						pAction->GetTitle(), m_pSettings->GetAppName(), pAction->GetTitle());					

					// Ask confirmation
					CConfirmationDlg dlgConfirm(m_pLanguage->GetString(IDS_CONFIRMATION_CLOSEAPPLICATION_TITLE),
						m_pLanguage->GetString(IDS_CONFIRMATION_TITLE), sTemp,
						m_pLanguage->GetString(IDS_GENERAL_YES), m_pLanguage->GetString(IDS_GENERAL_NO));
					if (dlgConfirm.DoModal() == IDNO)
					{
						// Send error message
						PostThreadMessage(WMU_ERROR, ERROR_CLOSEAPP, (LPARAM)(LPCTSTR)pAction->GetTitle());
						return;
					}
					else
					{
						// Close application imediately
						if (!m_pFunctions->CloseApplication(pAction->GetTitle(), pAction->GetFilename()))
						{
							// Send error message
							PostThreadMessage(WMU_ERROR, ERROR_CLOSEAPP, (LPARAM)(LPCTSTR)pAction->GetTitle());
							return;
						}
					}

					// TEMPORARY SOLUTION - END
				}
				else
				{
					// Close application imediately
					if (!m_pFunctions->CloseApplication(pAction->GetTitle(), pAction->GetFilename()))
					{
						// Send error message
						PostThreadMessage(WMU_ERROR, ERROR_CLOSEAPP, (LPARAM)(LPCTSTR)pAction->GetTitle());
						return;
					}
				}
			}
		}

		if (sType == EVENTACTION_SHOWNOTIFIER)
		{
			// Only valid in silent mode
			if (m_sMode != MODE_SILENT)
			{
				// Quit
				return;
			}

			// Get right action
			CEventActionShowNotifier * pAction = (CEventActionShowNotifier *)pEventData->GetAction(i);

			// Log
			m_pLog->Log(_T("handle eventaction SHOWNOTIFIER"));

			// Show notifier
			SendMessageToUI(WMU_SHOWNOTIFIER, (LPARAM)(LPCTSTR)pAction->GetTitle(),
				(LPARAM)(LPCTSTR)pAction->GetDescription());
		}
	}
}

//=====================================================================

bool CUpdateProcess::CreateShortcut(CString sTargetFile, CString sParameters,
								CString sLinkFile, CString sDescription, 
								int iShowMode, CString sWorkingDirectory,
								CString sIconFile, int iIconIndex)
{
	// Declare variables
	HRESULT        hRes;                  /* Returned COM result code */
	IShellLink *   pShellLink;            /* IShellLink object pointer */
	IPersistFile * pPersistFile;          /* IPersistFile object pointer */
	WCHAR          wszLinkfile[MAX_PATH]; /* pszLinkfile as Unicode string */
	bool	       bResult = true;

	// Create instance of IShellLink object
	hRes = CoCreateInstance(CLSID_ShellLink,      /* pre-defined CLSID of the IShellLink object */
							NULL,                 /* pointer to parent interface if part of aggregate */
							CLSCTX_INPROC_SERVER, /* caller and called code are in same process */
							IID_IShellLink,      /* pre-defined interface of the IShellLink object */
							(LPVOID*) &pShellLink);         /* Returns a pointer to the IShellLink object */

	// Check if we succeeded
	if (SUCCEEDED(hRes))
	{
		/* Set the fields in the IShellLink object */
		pShellLink->SetPath(sTargetFile);
		pShellLink->SetArguments(sParameters);

		if (sDescription.GetLength() > 0)
		{
			pShellLink->SetDescription(sDescription);
		}

		if (iShowMode > 0)
		{
			pShellLink->SetShowCmd(iShowMode);
		}

		if (lstrlen(sWorkingDirectory) > 0)
		{
			pShellLink->SetWorkingDirectory(sWorkingDirectory);
		}

		if (lstrlen(sIconFile) > 0 && iIconIndex >= 0)
		{
			pShellLink->SetIconLocation(sIconFile, iIconIndex);
		}

		/* Use the IPersistFile object to save the shell link */
		hRes = pShellLink->QueryInterface(IID_IPersistFile, /* pre-defined interface of the IPersistFile object */
										  (LPVOID *) &pPersistFile);    /* returns a pointer to the IPersistFile object */
		if (SUCCEEDED(hRes))
		{
			lstrcpy(wszLinkfile, sLinkFile);
			//iWideCharsWritten = MultiByteToWideChar(CP_ACP, 0, sLinkFile, -1, wszLinkfile, MAX_PATH);
			hRes = pPersistFile->Save(wszLinkfile, TRUE);
			pPersistFile->Release();
		}
		else
		{
			// We failed
			bResult = false;
		}
		pShellLink->Release();
	}
	else
	{
		// We failed
		bResult = false;
	}

	// Return result
	return bResult;
}

//=====================================================================

void CUpdateProcess::OnThreadMessage(WPARAM wParam, LPARAM lParam)
{
	// Declare variables
	int iThreadAction = wParam;
	int iTask = lParam;
	CString sTemp;

	// Log
	sTemp.Format(_T("CUpdateProcess::OnThreadMessage -> wParam = %d; lParam = %d"), iThreadAction, iTask);
	m_pLog->Log(sTemp);

	// Check what task to perform for thread
	switch (iThreadAction)
	{
	case THREAD_PAUSE:
		// Log
		m_pLog->Log(_T("CUpdateProcess::OnThreadMessage -> pause update process"));

		// Pause thread
		SuspendThread();
		break;

	case THREAD_RESUME:
		// Log
		m_pLog->Log(_T("CUpdateProcess::OnThreadMessage -> resume update process"));

		// Resume thread
		ResumeThread();
		break;

	case THREAD_STARTNEXTTASK:
		// Log
		m_pLog->Log(_T("CUpdateProcess::OnThreadMessage -> start next task"));

		// Start next task
		StartTask();
		break;

	case THREAD_SKIPNEXTTASK:
		// Log
		m_pLog->Log(_T("CUpdateProcess::OnThreadMessage -> skip next task"));

		// Skip next task
		m_iCurrentTask++;
		break;

	case THREAD_CANCEL:
		// Log
		m_pLog->Log(_T("CUpdateProcess::OnThreadMessage -> cancel update process"));

		// User cancelled update process
		m_bUpdateCancelled = true;

		// Stop current thread
		if (m_pCurrentThread)
		{
			// Stop thread
			m_pCurrentThread->PostThreadMessage(WMU_THREADMESSAGE, THREAD_CANCEL, 0);

			// Wait until it is really finished
			WaitForSingleObject(m_pCurrentThread->m_hThread, INFINITE);
		}

		// Only start if user is installing or further
		if (m_iCurrentTask >= TASK_INSTALL)
		{
			// Rollback changes
			StartTask(TASK_ROLLBACK);
		}
		else
		{
			// Just close Updater
			PostThreadMessage(WM_QUIT, 0, 0);
		}
		break;
		
	case THREAD_FINISH:
		// Log
		m_pLog->Log(_T("CUpdateProcess::OnThreadMessage -> finish update process"));

		// We are finished
		m_bFinished = true;
		
		// Exit thread
		PostThreadMessage(WM_QUIT, 0, 0);
		break;

	case THREAD_STARTTASK:
		// Log
		m_pLog->Log(_T("CUpdateProcess::OnThreadMessage -> start specific task"));

		// Check if task isn't running already
		if (iTask > m_iCurrentTask)
		{
			// Start a specific task
			StartTask(iTask);
		}
		break;
	}
}

//=====================================================================

void CUpdateProcess::OnTaskComplete(WPARAM wParam, LPARAM lParam)
{
	// Declare variables
	int iTask = (int)wParam;
	unsigned long ulCounter = 0;

	// Check if process is cancelled
	if (m_bUpdateCancelled)
	{
		// We are only allowed to handle TASK_ROLLBACK
		if (iTask != TASK_ROLLBACK)
			return;
	}

	if (!IsBadReadPtr(m_pCurrentThread, sizeof(CWinThread *)))
	{
		// Wait for the thread to be really closed, not forever,
        // be smart to come out of any stale state.
        while ((++ulCounter < 60) && !IsBadReadPtr(m_pCurrentThread, sizeof(CWinThread *)))
        {
			// Log
		    m_pLog->Log(_T("CUpdateProcess::OnTaskComplete - Waiting..."));

			// Now wait
    		if (WaitForSingleObject(m_pCurrentThread->m_hThread, 1000) == WAIT_OBJECT_0)
            {
                break;
            }
        }

		// Thread is closed, set to NULL
		m_pCurrentThread = NULL;
	}
	
	// Check what task is completed
	switch (iTask)
	{
	case TASK_INIT:
		// Log
		m_pLog->Log(_T("task ready: TASK_INIT"));
		// Check what user interface we should run in
		SetMode(m_pSettings->GetRunmode());
		break;

	case TASK_CHECKVERSION:
		// Log
		m_pLog->Log(_T("task ready: TASK_CHECKVERSION"));
		// Handle OnNewVersion event
		HandleEvent(m_pEventInfo->GetEventInfo(EVENT_ONNEWVERSION));
		break;

	case TASK_HTMLMESSAGE:
		// Log
		m_pLog->Log(_T("task ready: TASK_HTMLMESSAGE"));
		// Handle AfterHtmlMessage event
		HandleEvent(m_pEventInfo->GetEventInfo(EVENT_AFTERHTMLMESSAGE));
		// Start next task
		StartTask();
		break;

	case TASK_LICENSE:
		// Log
		m_pLog->Log(_T("task ready: TASK_LICENSE"));
		// Handle AfterLicense event
		HandleEvent(m_pEventInfo->GetEventInfo(EVENT_AFTERLICENSE));
		// Start next task
		StartTask();
		break;

	case TASK_PROTECTION:
		// Log
		m_pLog->Log(_T("task ready: TASK_PROTECTION"));
		// Handle AfterProtection event
		HandleEvent(m_pEventInfo->GetEventInfo(EVENT_AFTERPROTECTION));
		break;

	case TASK_SELECTUPDATE:
		// Select the updates (this is a little bit different because
		// this is needed after the user selected the updates)
		if (!SelectUpdate())
		{
			// Stop executing this thread
			PostThreadMessage(WM_QUIT, 0, 0);
			return;
		}

		// Wait until thread is ready
		if (!IsBadReadPtr(m_pCurrentThread, sizeof(CWinThread *)))
		{
			// Wait for the thread to be really closed
			WaitForSingleObject(m_pCurrentThread->m_hThread, INFINITE);

			// Thread is closed, set to NULL
			m_pCurrentThread = NULL;
		}

		// Log
		m_pLog->Log(_T("task ready: TASK_SELECTUPDATE"));
		// Handle AfterSelectUpdate event
		HandleEvent(m_pEventInfo->GetEventInfo(EVENT_AFTERSELECTUPDATE));
		// Start next task
		StartTask();
		break;

	case TASK_DOWNLOAD:
		// Log
		m_pLog->Log(_T("task ready: TASK_DOWNLOAD"));
		// Handle AfterDownload event
		HandleEvent(m_pEventInfo->GetEventInfo(EVENT_AFTERDOWNLOAD));
		break;

	case TASK_INSTALL:
		// Log
		m_pLog->Log(_T("task ready: TASK_INSTALL"));
		// Install is complete
		m_bInstallComplete = true;
		// Handle AfterInstall event
		HandleEvent(m_pEventInfo->GetEventInfo(EVENT_AFTERINSTALL));
		break;

	case TASK_ROLLBACK:
		// Log
		m_pLog->Log(_T("task ready: TASK_ROLLBACK"));
		// Install is not complete
		m_bInstallComplete = false;
		// Handle AfterRollback event
		HandleEvent(m_pEventInfo->GetEventInfo(EVENT_AFTERROLLBACK));
		break;

#if defined _DEBUG || defined _BETA
	default:
		// We have a problem, stop thread!
		AfxMessageBox(_T("OOPS! Unknown task!"));
		PostThreadMessage(WM_QUIT, 0, 0);
		break;
#endif
	}

	// Always send this message to user interface
	SendMessageToUI(WMU_TASK_COMPLETE, wParam, lParam);
}

//=====================================================================

void CUpdateProcess::OnError(WPARAM wParam, LPARAM lParam)
{
	// Declare variables
	int iError = (int)wParam;
	int iTask = (int)lParam;
	CString sTemp;

	// Check what error occurred
	switch (iError)
	{
	case ERROR_UNKNOWN:
		// Log
		m_pLog->Log(_T("ERROR: unknown error"));
		break;

	case ERROR_NOCONNECTION:
		// Log
		m_pLog->Log(_T("ERROR: no connection"));
		break;

	case ERROR_SERVER:
		// Log
		m_pLog->Log(_T("ERROR: server"));
		break;

	case ERROR_NODISKSPACE:
		// Log
		m_pLog->Log(_T("ERROR: no disk space"));
		break;

	case ERROR_FILENOTFOUND:
		// Log
		sTemp.Format(_T("ERROR: file %s not found"), lParam);
		m_pLog->Log(sTemp);
		break;

	case ERROR_COPYFILE:
		// Log
		sTemp.Format(_T("ERROR: copy file %s"), lParam);
		m_pLog->Log(sTemp);
		break;

	case ERROR_DELETEFILE:
		// Log
		sTemp.Format(_T("ERROR: delete file %s"), lParam);
		m_pLog->Log(sTemp);
		break;

	case ERROR_RUNFILE:
		// Log
		sTemp.Format(_T("ERROR: run file %s"), lParam);
		m_pLog->Log(sTemp);
		break;

	case ERROR_CLOSEAPP:
		// Log
		m_pLog->Log(_T("ERROR: close app"));
		break;

	case ERROR_FILECORRUPT:
		// Log
		sTemp.Format(_T("ERROR: file %s corrupt"), lParam);
		m_pLog->Log(sTemp);
		break;

	case ERROR_NONEWVERSION:
		// Log
		m_pLog->Log(_T("No new version is found"));
		// Handle OnNoNewVersion event
		HandleEvent(m_pEventInfo->GetEventInfo(EVENT_ONNONEWVERSION));
		break;

	case ERROR_WRITEFILE:
		// Log
		sTemp.Format(_T("ERROR: write file %s"), lParam);
		m_pLog->Log(sTemp);
		break;

	case ERROR_UNZIPFILE:
		// Log
		sTemp.Format(_T("ERROR: unzip file %s"), lParam);
		m_pLog->Log(sTemp);
		break;

	case ERROR_PROTECTION:
		// Log
		m_pLog->Log(_T("ERROR: protection test failed"));
		break;
	}

	// First send message to user interface
	SendMessageToUI(WMU_ERROR, wParam, lParam);
}

//=====================================================================

void CUpdateProcess::OnWarning(WPARAM wParam, LPARAM lParam)
{
	// Declare variables
	int iWarning = (int)wParam;
	int iTask = (int)lParam;
	CString sTemp;

	// Check what warning is given
	switch (iWarning)
	{
	case WARNING_REGISTER:
		// Log
		sTemp.Format(_T("WARNING: file %s not registered"), lParam);
		m_pLog->Log(sTemp);
		break;

#if defined _DEBUG || defined _BETA
	default:
		// We have a problem, no unknown warning should be sent!
		AfxMessageBox(_T("WARNING: unknown warning, this is bad!"));
		PostThreadMessage(WM_QUIT, 0, 0);
		break;
#endif
	}

	// Send message to user interface
	SendMessageToUI(WMU_WARNING, wParam, lParam);
}

//=====================================================================

void CUpdateProcess::OnWarningComplete(WPARAM wParam, LPARAM lParam)
{
	// Send to thread so it can continue
	m_pCurrentThread->PostThreadMessage(WMU_WARNING_COMPLETE, wParam, lParam);
}

//=====================================================================

void CUpdateProcess::OnQuestion(WPARAM wParam, LPARAM lParam)
{
	// Declare variables
	int iQuestion = (int)wParam;
	CString sTemp;

	// Log question
	sTemp.Format(_T("QUESTION_ID: %d"), iQuestion);
	m_pLog->Log(sTemp);

	// Send message to user interface
	SendMessageToUI(WMU_QUESTION, wParam, lParam);
}

//=====================================================================

void CUpdateProcess::OnQuestionAnswer(WPARAM wParam, LPARAM lParam)
{
	// Declare variables
	int iQuestion = (int)wParam;
	int iAnswer = (int)lParam;
	CString sTemp;

	// Log question answer
	sTemp.Format(_T("QUESTIONANSWER_ID: question: %d -> answer: %d"), iQuestion, iAnswer);
	m_pLog->Log(sTemp);

	// Are we handling an event?
	/*if (m_bEventHandling)
	{
		// Handle ourself
		if (lParam == QUESTIONRESULT_YES)
		{
			// Close application
			if (!m_pFunctions->CloseApplication(m_pSettings->GetAppLocation()))
			{
				// Send error message
				PostThreadMessage(WMU_ERROR, ERROR_CLOSEAPP, (LPARAM)(LPCTSTR)m_pSettings->GetAppName());
				return;
			}

			// Continue
			
		}
		else
		{
			// Send error message
			PostThreadMessage(WMU_ERROR, ERROR_CLOSEAPP, (LPARAM)(LPCTSTR)m_pSettings->GetAppName());
			return;
		}

		// Unset boolean
		m_bEventHandling = false;
	}
	else
	{*/
	// Send message to thread so it can continue
	m_pCurrentThread->PostThreadMessage(WMU_QUESTION_ANSWER, wParam, lParam);
}

//=====================================================================

void CUpdateProcess::OnUpdateGUI(WPARAM wParam, LPARAM lParam)
{
	// Send message to user interface
	SendMessageToUI(WMU_UPDATE_GUI, wParam, lParam);
}

//=====================================================================

void CUpdateProcess::OnUpdateGUIFileProgress(WPARAM wParam, LPARAM lParam)
{
	// Send message to user interface
	SendMessageToUI(WMU_UPDATE_GUI_FILEPROGRESS, wParam, lParam);
}

//=====================================================================

void CUpdateProcess::OnUpdateGUITotalProgress(WPARAM wParam, LPARAM lParam)
{
	// Send message to user interface
	SendMessageToUI(WMU_UPDATE_GUI_TOTALPROGRESS, wParam, lParam);
}

//=====================================================================

void CUpdateProcess::OnUpdateGUIProtection(WPARAM wParam, LPARAM lParam)
{
	// Are we stopped yet?
	if (!m_bUpdateCancelled)
	{
		// Send message to user interface
		SendMessageToUI(WMU_UPDATE_GUI_PROTECTION, wParam, lParam);
	}
}

//=====================================================================

void CUpdateProcess::OnSwitchUI(WPARAM wParam, LPARAM lParam)
{
	// Declare variables
	CString sUI;

	// Get user interface
	sUI.Format(_T("%s"), wParam);

	// Switch to right user interface
	SetMode(sUI);

	// Send message to UI
	SendMessageToUI(m_oLastUIMessage.message, m_oLastUIMessage.lParam, m_oLastUIMessage.wParam, false); 
}

//=====================================================================

void CUpdateProcess::OnShowAbout(WPARAM wParam, LPARAM lParam)
{
	// If current thread is valid, pause it
	if (m_pCurrentThread)
		m_pCurrentThread->SuspendThread();

	// Log
	m_pLog->Log(_T("ABOUT: user wants to see about dialog"));

	// Show about box
	CAboutDlg dlgAbout;
	dlgAbout.DoModal();

	// Log
	m_pLog->Log(_T("ABOUT: user closed about dialog"));

	// Resume thread
	if (m_pCurrentThread)
		m_pCurrentThread->ResumeThread();
}